{
"cells": [
{
"cell_type": "markdown",
"id": "d26e3b44",
"metadata": {},
"source": [
"# Decomposing Qiskit circuits"
]
},
{
"cell_type": "markdown",
"id": "f5994d25",
"metadata": {},
"source": [
"In this notebook, we show how Qiskit circuits can be converted into Perceval circuits. To do so, we take the example of a simple gate-based circuit (a circuit producing GHZ states) and we show the translation to a linear optical circuit. We also show the equivalence between the two circuits."
]
},
{
"cell_type": "markdown",
"id": "3ed1c79e",
"metadata": {},
"source": [
"As usual we start by importing the needed libraries. Note that this notebook requires the installation of Qiskit (which can be easiliy done with `pip install qiskit`)."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9dea723b",
"metadata": {},
"outputs": [],
"source": [
"import perceval as pcvl\n",
"from perceval.components import catalog\n",
"from perceval.converters import QiskitConverter\n",
"from perceval.algorithm import Analyzer, Sampler\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import Statevector"
]
},
{
"cell_type": "markdown",
"id": "61e8fe05",
"metadata": {},
"source": [
"## GHZ State generation in Qiskit"
]
},
{
"cell_type": "markdown",
"id": "57423a74",
"metadata": {},
"source": [
"We first define the circuit generating GHZ states of 3 qubits with Qiskit. To do so, we first act with a Hadamard gate on qubit 0 to put in superposition of state $|0\\rangle$ and $|1\\rangle$. Then we perform two CNOT gates using qubit 0 as control and qubits 1 and 2 as targets."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "aef291d3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
┌───┐ \n",
"q_0: ┤ H ├──■────■──\n",
" └───┘┌─┴─┐ │ \n",
"q_1: ─────┤ X ├──┼──\n",
" └───┘┌─┴─┐\n",
"q_2: ──────────┤ X ├\n",
" └───┘
"
],
"text/plain": [
" ┌───┐ \n",
"q_0: ┤ H ├──■────■──\n",
" └───┘┌─┴─┐ │ \n",
"q_1: ─────┤ X ├──┼──\n",
" └───┘┌─┴─┐\n",
"q_2: ──────────┤ X ├\n",
" └───┘"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Create a Quantum Circuit acting on the q register\n",
"circuit = QuantumCircuit(3)\n",
"\n",
"# Add a H gate on qubit 0\n",
"circuit.h(0)\n",
"\n",
"# Add CX (CNOT) gates on control qubit 0 and target qubits 1 and 2\n",
"circuit.cx(0, 1)\n",
"circuit.cx(0, 2)\n",
"\n",
"# Draw the circuit\n",
"circuit.draw()"
]
},
{
"cell_type": "markdown",
"id": "cca607ca",
"metadata": {},
"source": [
"We display the final state when starting from the input state $|000\\rangle$."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7edaa2b3",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$$\\frac{\\sqrt{2}}{2} |000\\rangle+\\frac{\\sqrt{2}}{2} |111\\rangle$$"
],
"text/plain": [
""
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Set the initial state of the simulator to the ground state using from_int\n",
"state = Statevector.from_int(0, 2**3)\n",
"\n",
"# Evolve the state by the quantum circuit\n",
"state = state.evolve(circuit)\n",
"\n",
"#draw using latex\n",
"state.draw('latex')"
]
},
{
"cell_type": "markdown",
"id": "9a4a941f",
"metadata": {},
"source": [
"## Conversion to Perceval"
]
},
{
"cell_type": "markdown",
"id": "9d6365aa",
"metadata": {},
"source": [
"With the use of `QiskitConverter`, we can transform the Qiskit circuit into a Perceval circuit. It uses 2 modes per qubit and additional modes for ancillary photons to perform deterministically two-qubit gates. Below the first six modes correspond to the three logical qubits (see the 'Spatial Modes encoding' paragraph in the 'Basics' section of the documentation) of the gate-based circuit above.\n",
"\n",
"The other modes are used to successfully implement two-qubit gates via heralding or post-selection. Heralding employs [4 ancillary modes](https://doi.org/10.1073/pnas.1018839108) while post-selection employs [2 ancillary modes](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.65.062324). With the option `use_postselection=True` in the method `.convert` on a `QiskitConverter` object, every CNOT but the last is implemented with a heralding scheme. Here it means that it would add $4+2$ ancillary modes. The option `use_postselection=False` only implements heralded CNOTs. Here it would mean $4+4$ ancillary modes. Note: the `use_postselection` option is `True` by default."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "dccbd8c9",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qiskit_converter = QiskitConverter(catalog, backend_name=\"Naive\")\n",
"quantum_processor = qiskit_converter.convert(circuit, use_postselection=True)\n",
"pcvl.pdisplay(quantum_processor, recursive=True)"
]
},
{
"cell_type": "markdown",
"id": "88ff50bd",
"metadata": {},
"source": [
"With this converted circuit, we can now check that the resulting state is the same as before the conversion. By default, the input is the logical state $|000\\rangle_L$. Note that where Qiskit displays state in the order $|q_2q_1q_0\\rangle_L$, Perceval uses the reverse order $|q_0q_1q_2\\rangle_L$, but still shown as Fock states. Here, it doesn't change anything since we end with only $|000\\rangle_L$ and $|111\\rangle_L$ states."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8ddb1457",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
state
probability
\n",
"\n",
"\n",
"
|1,0,1,0,1,0>
1/2
\n",
"
|0,1,0,1,0,1>
1/2
\n",
"
|1,0,0,1,1,0>
0
\n",
"
|0,1,1,0,0,1>
0
\n",
"\n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Not necessary here\n",
"quantum_processor.with_input(pcvl.LogicalState([0,0,0]))\n",
"\n",
"sampler = Sampler(quantum_processor)\n",
"\n",
"output_distribution = sampler.probs()[\"results\"]\n",
"pcvl.pdisplay(output_distribution, precision=1e-2, max_v = 4)"
]
},
{
"cell_type": "markdown",
"id": "2f14a1c5",
"metadata": {},
"source": [
"This circuit can now be converted using a general interferometer decomposition so it can be implemented on a generic photonic chip."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "d15ff2ba",
"metadata": {},
"outputs": [],
"source": [
"u = quantum_processor.linear_circuit().compute_unitary(use_symbolic=False)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "1de600cb",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ub = (pcvl.Circuit(2)\n",
" // pcvl.BS(theta=pcvl.Parameter(\"theta\"))\n",
" // (0, pcvl.PS(phi=pcvl.Parameter(\"φ_a\"))))\n",
"\n",
"pc_norm = pcvl.Circuit.decomposition(u, ub, shape=\"triangle\")\n",
"pcvl.pdisplay(pc_norm, compact=True, render_size=0.5)"
]
},
{
"cell_type": "markdown",
"id": "ff0f021c",
"metadata": {},
"source": [
"## A cnot based on CZ\n",
"\n",
"Another interesting example we can explore is how to build a cnot from a CZ gate using qiskit then convert it to Perceval. We will apply the following equivalence:\n",
"\n",
"\n",
"\n",
"\n",
"The code in Qiskit:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "6516a88a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"q_0: ──────■──────\n",
" ┌───┐ │ ┌───┐\n",
"q_1: ┤ H ├─■─┤ H ├\n",
" └───┘ └───┘
"
],
"text/plain": [
" \n",
"q_0: ──────■──────\n",
" ┌───┐ │ ┌───┐\n",
"q_1: ┤ H ├─■─┤ H ├\n",
" └───┘ └───┘"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"circuit = QuantumCircuit(2)\n",
"\n",
"# Add (CNOT) built using equivalence with H-CZ-H\n",
"circuit.h(1)\n",
"circuit.cz(0, 1)\n",
"circuit.h(1)\n",
"# Draw the circuit\n",
"circuit.draw()"
]
},
{
"cell_type": "markdown",
"id": "bcff0e53",
"metadata": {},
"source": [
"Then we call the converter like the previous example"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "94b97e12",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
|1,0,1,0>
|1,0,0,1>
|0,1,1,0>
|0,1,0,1>
\n",
"\n",
"\n",
"
|1,0,1,0>
1
0
0
0
\n",
"
|1,0,0,1>
0
1
0
0
\n",
"
|0,1,1,0>
0
0
0
1
\n",
"
|0,1,0,1>
0
0
1
0
\n",
"\n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"state = Statevector.from_int(0, 2**3)\n",
"state = state.evolve(circuit)\n",
"\n",
"qiskit_converter = QiskitConverter(catalog, backend_name=\"SLOS\")\n",
"quantum_processor = qiskit_converter.convert(circuit)\n",
"# pcvl.pdisplay(quantum_processor, recursive=True) # the perceval processor can be displayed at this point if needed\n",
"\n",
"input_states = [pcvl.BasicState([1, 0, 1, 0]), pcvl.BasicState([1, 0, 0, 1]), pcvl.BasicState([0, 1, 1, 0]), pcvl.BasicState([0, 1, 0, 1])]\n",
"analyzer = Analyzer(quantum_processor, input_states)\n",
"pcvl.pdisplay(analyzer)"
]
},
{
"cell_type": "markdown",
"id": "3d79ee37",
"metadata": {},
"source": [
"This is the truth table of a CNOT gate\n",
"\n",
"## Few remarks\n",
"- Controlflow operations such as measurement operator in the qiskit ciruit or `qiskit.circuit.QuantumCircuit.if_test` are not supported.\n",
"- Custom gates are also not supported at the moment (see [Issue#201](https://github.com/Quandela/Perceval/issues/201))."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}